from typing import List, Optional, Union, Tuple
import numpy as np
import cv2
import win32gui
import win32ui
import win32con
from win32api import GetSystemMetrics
from PIL import Image
from .engine.base import BaseOcrEngine, OcrResult
from utils.window.window_manager import WindowManager
from .engine.easy_ocr import EasyOcrEngine
from utils.log.logging import logger
from enum import Enum
from .function.capture import capture_window

class CaptureAnchor(Enum):
    LEFT = "left"
    RIGHT = "right"
    TOP = "top"
    BOTTOM = "bottom"
    LEFT_TOP = "left_top"
    RIGHT_TOP = "right_top"
    LEFT_BOTTOM = "left_bottom"
    RIGHT_BOTTOM = "right_bottom"
    CENTER = "center"

class GlobalOcr:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(GlobalOcr, cls).__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not hasattr(self, 'initialized'):
            self.initialized = True
            self._engine = EasyOcrEngine(['ch_sim', 'en'])
            self._window_manager = WindowManager()
    
    @property
    def engine(self) -> BaseOcrEngine:
        """获取当前OCR引擎"""
        return self._engine
    
    def set_engine(self, engine: BaseOcrEngine):
        """设置OCR引擎"""
        self._engine = engine
        

        
    
    def recognize(self, image: np.ndarray, confidence_threshold: float = 0.5, 
                 target_color: str = None) -> List[OcrResult]:
        """
        识别图片中的文字
        
        Args:
            image: 图片数据
            confidence_threshold: 置信度阈值
            target_color: 目标颜色 ('black', 'white', None)
            
        Returns:
            List[OcrResult]: 识别结果列表
        """
        # 颜色范围预设
        COLOR_RANGES = {
            'black': {
                'lower': np.array([0, 0, 0]),
                'upper': np.array([180, 255, 46])
            },
            'white': {
                'lower': np.array([0, 0, 221]),
                'upper': np.array([180, 30, 255])
            }
        }
        
        # 如果指定了目标颜色,只保留该颜色
        if target_color and target_color in COLOR_RANGES:
            # 转换为HSV色彩空间
            hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
            
            # 获取指定颜色范围
            color_mask = cv2.inRange(
                hsv,
                COLOR_RANGES[target_color]['lower'],
                COLOR_RANGES[target_color]['upper']
            )
            
            # 只保留目标颜色
            image = cv2.bitwise_and(image, image, mask=color_mask)
        
        return self.engine.recognize(image, confidence_threshold)
    
    def find_text(self, image: np.ndarray, target_text: str, 
                 confidence_threshold: float = 0.5,
                 target_color: str = None) -> Optional[OcrResult]:
        """
        在图片中查找指定文字
        
        Args:
            image: 图片数据
            target_text: 要查找的文字
            confidence_threshold: 置信度阈值
            target_color: 目标颜色 ('black', 'white', None)
            
        Returns:
            Optional[OcrResult]: 找到的文字结果，未找到返回None
        """
        results = self.recognize(image, confidence_threshold, target_color)
        for result in results:
            if target_text in result.text:
                return result
        return None
    
    def capture_and_recognize(self,
                            hwnd: int,
                            start_point: Tuple[Union[int, float, str], Union[int, float, str]] = (0, 0),
                            end_point: Optional[Tuple[Union[int, float, str], Union[int, float, str]]] = None,
                            size: Optional[Tuple[Union[int, float, str], Union[int, float, str]]] = None,
                            anchor: Union[str, CaptureAnchor] = CaptureAnchor.LEFT_TOP,
                            debug: bool = False,
                            confidence_threshold: float = 0.5) -> List[OcrResult]:
        """
        截图并识别文字
        
        Args:
            hwnd: 窗口句柄
            start_point: 起始点 (x, y)，支持像素值或百分比
            end_point: 结束点 (x, y)，支持像素值或百分比
            size: 区域大小 (width, height)，支持像素值或百分比
            anchor: 锚点位置
            debug: 是否显示调试框
            confidence_threshold: 置信度阈值
            
        Returns:
            List[OcrResult]: 识别结果列表
        """
        img = self.capture_window(hwnd, start_point, end_point, size, anchor, debug)
        if img is not None:
            return self.recognize(img, confidence_threshold)
        return []
    
    def capture_window(self,
                      hwnd: int,
                      start_point: Tuple[Union[int, float, str], Union[int, float, str]] = (0, 0),
                      end_point: Optional[Tuple[Union[int, float, str], Union[int, float, str]]] = None,
                      size: Optional[Tuple[Union[int, float, str], Union[int, float, str]]] = None,
                      anchor: Union[str, CaptureAnchor] = CaptureAnchor.LEFT_TOP,
                      debug: bool = False) -> Optional[np.ndarray]:
        """
        截取窗口指定区域
        
        Args:
            hwnd: 窗口句柄
            start_point: 起始点 (x, y)，支持:
                - 像素值: (100, 100)
                - 百分比字符串: ("10%", "20%")
                - 小数百分比: (0.1, 0.2)
            end_point: 结束点 (x, y)，格式同start_point
            size: 区域大小 (width, height)，格式同start_point
            anchor: 锚点位置，支持:
                - LEFT: 左对齐
                - RIGHT: 右对齐
                - TOP: 顶部对齐
                - BOTTOM: 底部对齐
                - LEFT_TOP: 左上角
                - RIGHT_TOP: 右上角
                - LEFT_BOTTOM: 左下角
                - RIGHT_BOTTOM: 右下角
                - CENTER: 中心点
            debug: 是否显示调试框
                
        Returns:
            Optional[np.ndarray]: 截图数据，失败返回None
            
        Examples:
            # 1. 使用像素值
            img = ocr.capture_window(hwnd, start_point=(100, 100), size=(200, 200))
            
            # 2. 使用百分比
            img = ocr.capture_window(
                hwnd, 
                start_point=("10%", "10%"), 
                size=("20%", "20%"),
                anchor="right_bottom"
            )
            
            # 3. 使用起点和终点
            img = ocr.capture_window(
                hwnd,
                start_point=(0.1, 0.1),
                end_point=(0.3, 0.3),
                anchor=CaptureAnchor.CENTER
            )
            
            # 4. 开启调试模式
        img = ocr.capture_window(
            hwnd,
            start_point=(100, 100),
            size=(200, 200),
            debug=True  # 会在窗口上绘制红色框
        )
    """
        
        # 调用截图函数
        return capture_window(self._window_manager.find_window(hwnd), start_point, end_point, size, anchor, debug)

    def capture_and_recognize(self,
                            hwnd: int,
                            start_point: Tuple[Union[int, float, str], Union[int, float, str]] = (0, 0),
                            end_point: Optional[Tuple[Union[int, float, str], Union[int, float, str]]] = None,
                            size: Optional[Tuple[Union[int, float, str], Union[int, float, str]]] = None,
                            anchor: Union[str, CaptureAnchor] = CaptureAnchor.LEFT_TOP,
                            debug: bool = False,
                            confidence_threshold: float = 0.5) -> List[OcrResult]:
        """
        截图并识别文字
        
        Args:
            参数同 capture_window，另加:
            confidence_threshold: 置信度阈值
                
        Returns:
            List[OcrResult]: 识别结果列表
        """
        img = self.capture_window(hwnd, start_point, end_point, size, anchor, debug)
        if img is not None:
            return self.recognize(img, confidence_threshold)
        return []

    @staticmethod
    def window_capture(hwnd, start_point=(0, 0), size=None, debug=False) -> np.ndarray:
        """
        截取指定窗口的指定区域
        
        Args:
            hwnd: 窗口句柄
            start_point: 起始点(x, y)
            size: 截图大小(width, height)，如果为None则截取整个窗口
            debug: 是否保存调试图片
        
        Returns:
            numpy.ndarray: BGR格式的图像数据
        """
        try:
            # 获取窗口大小
            left, top, right, bottom = win32gui.GetWindowRect(hwnd)
            width = right - left
            height = bottom - top
            
            # 如果没有指定大小，使用整个窗口大小
            if size is None:
                size = (width, height)
            
            # 创建设备上下文
            hwnd_dc = win32gui.GetWindowDC(hwnd)
            mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
            save_dc = mfc_dc.CreateCompatibleDC()
            
            # 创建位图对象
            save_bitmap = win32ui.CreateBitmap()
            save_bitmap.CreateCompatibleBitmap(mfc_dc, size[0], size[1])
            save_dc.SelectObject(save_bitmap)
            
            # 截图
            save_dc.BitBlt((0, 0), size, mfc_dc, start_point, win32con.SRCCOPY)
            
            # 获取位图信息
            bmp_info = save_bitmap.GetInfo()
            bmp_str = save_bitmap.GetBitmapBits(True)
            
            # 转换为numpy数组
            img = np.frombuffer(bmp_str, dtype='uint8')
            img.shape = (bmp_info['bmHeight'], bmp_info['bmWidth'], 4)
            
            # 清理资源
            win32gui.DeleteObject(save_bitmap.GetHandle())
            save_dc.DeleteDC()
            mfc_dc.DeleteDC()
            win32gui.ReleaseDC(hwnd, hwnd_dc)
            
            # 转换为BGR格式
            img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
            
            # 如果是调试模式，保存截图
            if debug:
                cv2.imwrite('debug_capture.png', img)
            
            return img
            
        except Exception as e:
            logger.error(f"截图失败: {e}")
            return np.array([])
    def capture_and_recognize(self, hwnd, start_point=(0, 0), end_point=None, 
                            size=None, debug=False, confidence_threshold=0.5):
        try:
            img = self.window_capture(hwnd, start_point, size, debug)
            
            if img.size == 0:
                raise ValueError("截图失败，获取到空图像")
            
            # 1. 转换到灰度图
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            
            # 2. 图像放大处理
            scaled = cv2.resize(gray, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
            
            # 3. 自适应二值化
            binary = cv2.adaptiveThreshold(
                scaled,
                255,
                cv2.ADAPTIVE_THRESH_MEAN_C,
                cv2.THRESH_BINARY,
                15,  # 减小块大小
                2    # 减小常数
            )
            
            # 4. 轻微的形态学操作
            kernel = np.ones((2,2), np.uint8)
            processed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
            
            if debug:
                cv2.imwrite('debug_preprocessed.png', processed)
            
            # 5. OCR配置
            custom_config = r'--oem 3 --psm 7 -c tessedit_char_whitelist=0123456789'
            
            # 6. OCR识别
            results = self.recognize(processed, confidence_threshold, custom_config)
            
            if results:
                texts = [result.text for result in results]
                numbers = ''.join(filter(str.isdigit, ''.join(texts)))
                return numbers
            
            return ""
            
        except Exception as e:
            logger.error(f"识别失败: {e}")
            return ""

    def _filter_color(self, image: np.ndarray, target_color: str) -> np.ndarray:
        """颜色过滤处理"""
        COLOR_RANGES = {
            'black': {
                'lower': np.array([0, 0, 0]),
                'upper': np.array([180, 255, 46])
            },
            'white': {
                'lower': np.array([0, 0, 221]),
                'upper': np.array([180, 30, 255])
            }
        }
        
        if target_color in COLOR_RANGES:
            hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
            mask = cv2.inRange(
                hsv,
                COLOR_RANGES[target_color]['lower'],
                COLOR_RANGES[target_color]['upper']
            )
            return cv2.bitwise_and(image, image, mask=mask)
        return image



class ColorNumberRecognizer:
    """颜色数字识别器"""
    
    def __init__(self, global_ocr: GlobalOcr):
        self.ocr = global_ocr
        
    def _preprocess_image(self, img: np.ndarray) -> np.ndarray:
        """
        优化的图像预处理，专门处理白色数字
        """
        # 转换到灰度图
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        
        # 使用高斯模糊减少噪点
        blurred = cv2.GaussianBlur(gray, (3, 3), 0)
        
        # 使用局部自适应阈值处理
        binary = cv2.adaptiveThreshold(
            blurred,
            255,
            cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
            cv2.THRESH_BINARY,
            11,  # 邻域大小
            2    # 常数
        )
        
        # 反转图像（使数字为黑色）
        binary = cv2.bitwise_not(binary)
        
        # 进行形态学操作
        kernel = np.ones((2, 2), np.uint8)
        processed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
        
        return processed

    def recognize_number(self, 
                        hwnd: int,
                        start_point: Tuple[int, int] = (0, 0),
                        size: Optional[Tuple[int, int]] = None,
                        debug: bool = False,
                        confidence_threshold: float = 0.5) -> str:
        try:
            # 计算size
            # size = None
            # if end_point:
            #     size = (end_point[0] - start_point[0], 
            #            end_point[1] - start_point[1])
            
            # 获取截图
            img = self.ocr.window_capture(hwnd, start_point, size, debug)
            
            if img.size == 0:
                raise ValueError("截图失败，获取到空图像")
            
            if debug:
                cv2.imwrite('debug_capture.png', img)
            
            # 1. 图像预处理
            processed = self._preprocess_image(img)
            
            if debug:
                cv2.imwrite('debug_processed.png', processed)
            
            # 2. 图像放大
            scaled = cv2.resize(processed, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
            
            if debug:
                cv2.imwrite('debug_scaled.png', scaled)
            
            # OCR识别
            results = self.ocr.recognize(scaled, confidence_threshold)
            
            # 提取数字
            if results:
                texts = [result.text for result in results]
                numbers = ''.join(filter(str.isdigit, ''.join(texts)))
                logger.info(f"识别到的原始文本: {texts}, 提取的数字: {numbers}")
                return numbers if numbers else ""
            
            return ""
            
        except Exception as e:
            logger.error(f"数字识别失败: {e}")
            return ""